﻿using System;
using System.Collections.Generic;
using System.IO;
using PSE.Deployment.Engine.Enums;
using PSE.Deployment.Engine.Interfaces;
using PSE.Deployment.SetupBase.Configuration;

namespace PSE.Deployment.SetupBase.Steps
{
    public class InstallAssembly : SetupStep<Configuration.Actions.InstallAssembly>
    {
        public InstallAssembly(IStepGroup parent, Group group, Configuration.Actions.InstallAssembly action, Parameter.Enums.SetupMode setupMode)
            : base(parent, group, action, setupMode)
        {
            this.ErrorBehavior = StepErrorBehavior.Ignore;
            this.ResetProgress();
        }

        public override void Execute()
        {
            this.ResetProgress();

            if (this.SetupMode == Parameter.Enums.SetupMode.Install)
            {
                InstallAssemblies();
            }
            else if (this.SetupMode == Parameter.Enums.SetupMode.Uninstall)
            {
                UninstallArtefacts();
            }
        }

        public override void Undo()
        {
            if (this.SetupMode == Parameter.Enums.SetupMode.Install)
            {
                UninstallArtefacts();
            }
        }

        private void ResetProgress()
        {
            int count = 0;

            foreach (FolderInfo folderInfo in Action.Folders)
            {
                string path;

                if (Directory.Exists(SetupMainFactory.GetInstance().Parameter.InstalationSource))
                    path = Path.Combine(SetupMainFactory.GetInstance().Parameter.InstalationSource, folderInfo.Source);
                else
                    path = Path.Combine(SetupMainFactory.GetInstance().Parameter.InstallationPath, folderInfo.Destination);

                if (folderInfo.Files.Count == 0)
                {
                    count += PSE.Deployment.Util.File.CountFiles(path, "*.dll");
                }
                else
                {
                    foreach (string file in folderInfo.Files)
                    {
                        if (Path.GetExtension(file).ToLower() == ".dll")
                            count++;
                    }
                }
            }

            this.ResetProgress(count);
        }

        private void InstallAssemblies()
        {
            foreach (FolderInfo folderInfo in Action.Folders)
            {
                string path = Path.Combine(SetupMainFactory.GetInstance().Parameter.InstalationSource, folderInfo.Source);

                if (folderInfo.Files.Count == 0)
                {
                    if (Directory.Exists(path))
                        InstallAssemblies(path, Directory.GetFiles(path, "*.dll", SearchOption.AllDirectories));
                }
                else
                {
                    InstallAssemblies(path, PSE.Deployment.Util.File.GetFiles(path, folderInfo.Files));
                }
            }
        }

        private void InstallAssemblies(string path, IEnumerable<string> files)
        {
            foreach (string file in files)
            {
                if (this.IsStopped())
                    break;

                string assembly;

                if (Path.IsPathRooted(file))
                {
                    assembly = file;
                }
                else
                {
                    assembly = Path.Combine(path, file);
                }

                if (assembly.EndsWith("dll"))
                {
                    PSE.Deployment.Util.GacUtil.UninstallAssembly(assembly);

                    this.IncrementProgress(assembly);

                    string fullName;

                    try
                    {
                        fullName = System.Reflection.Assembly.LoadFrom(assembly).FullName.Replace("PublicKeyToken", "Public Key Token");
                    }
                    catch (Exception ex)
                    {
                        fullName = Path.GetFileNameWithoutExtension(assembly);

                        SetupMainFactory.GetInstance().WriteStepLog(this, string.Format("Não foi possível extrair o Full Name do assembly '{0}'", assembly), ex);
                    }

                    this.AddUsedBy(fullName);

                    if (!PSE.Deployment.Util.GacUtil.InstallAssembly(assembly))
                    {
                        SetupMainFactory.GetInstance().WriteStepLog(this, string.Format("Não foi possível instalar o assembly '{0}' no GAC", assembly));
                    }
                }
            }
        }

        protected override void UninstallArtefact(string artefactName)
        {
            uint pulDisposition;

            if (!PSE.Deployment.Util.GacUtil.UninstallAssembly(artefactName, out pulDisposition))
            {
                PSE.Deployment.Util.GacUtil.IASSEMBLYCACHE_UNINSTALL_DISPOSITION result = (PSE.Deployment.Util.GacUtil.IASSEMBLYCACHE_UNINSTALL_DISPOSITION)pulDisposition;

                if (result == PSE.Deployment.Util.GacUtil.IASSEMBLYCACHE_UNINSTALL_DISPOSITION.IASSEMBLYCACHE_UNINSTALL_DISPOSITION_HAS_INSTALL_REFERENCES ||
                    result == PSE.Deployment.Util.GacUtil.IASSEMBLYCACHE_UNINSTALL_DISPOSITION.IASSEMBLYCACHE_UNINSTALL_DISPOSITION_STILL_IN_USE)
                {
                    SetupMainFactory.GetInstance().WriteStepLog(this, string.Format("Não foi possível desinstalar o assembly '{0}' do GAC pois o mesmo está em uso", artefactName));
                }
            }
        }
    }
}
